package com.wmenjoy.utils.lang.util;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.wmenjoy.utils.lang.reflect.ClassUtil;

/**
 * @author ding.lid
 */
public final class CompatibleTypeUtils {

	private CompatibleTypeUtils() {
	}

	private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

	/**
	 * 兼容类型转换。null值是OK的。如果不需要转换，则返回原来的值。 进行的兼容类型转换如下：（基本类对应的Wrapper类型不再列出。）
	 * <ul>
	 * <li>String -> char, enum, Date
	 * <li>byte, short, int, long -> byte, short, int, long
	 * <li>float, double -> float, double
	 * </ul>
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Object compatibleTypeConvert(final Object value,
			final Class<?> type) {
		if (value == null || type == null
				|| type.isAssignableFrom(value.getClass())) {
			return value;
		}
		if (value instanceof String) {
			final String string = (String) value;
			if (char.class.equals(type) || Character.class.equals(type)) {
				if (string.length() != 1) {
					throw new IllegalArgumentException(
							String.format(
									"CAN NOT convert String(%s) to char!"
											+ " when convert String to char, the String MUST only 1 char.",
									string));
				}
				return string.charAt(0);
			} else if (type.isEnum()) {
				return Enum.valueOf((Class<Enum>) type, string);
			} else if (type == BigInteger.class) {
				return new BigInteger(string);
			} else if (type == BigDecimal.class) {
				return new BigDecimal(string);
			} else if (type == Short.class || type == short.class) {
				return new Short(string);
			} else if (type == Integer.class || type == int.class) {
				return new Integer(string);
			} else if (type == Long.class || type == long.class) {
				return new Long(string);
			} else if (type == Double.class || type == double.class) {
				return new Double(string);
			} else if (type == Float.class || type == float.class) {
				return new Float(string);
			} else if (type == Byte.class || type == byte.class) {
				return new Byte(string);
			} else if (type == Boolean.class || type == boolean.class) {
				return new Boolean(string);
			} else if (type == Date.class) {
				try {
					return new SimpleDateFormat(DATE_FORMAT)
							.parse((String) value);
				} catch (final ParseException e) {
					throw new IllegalStateException("Failed to parse date "
							+ value + " by format " + DATE_FORMAT + ", cause: "
							+ e.getMessage(), e);
				}
			} else if (type == Class.class) {
				try {
					return ClassUtil.name2class((String) value);
				} catch (final ClassNotFoundException e) {
					throw new RuntimeException(e.getMessage(), e);
				}
			}
		} else if (value instanceof Number) {
			final Number number = (Number) value;
			if (type == byte.class || type == Byte.class) {
				return number.byteValue();
			} else if (type == short.class || type == Short.class) {
				return number.shortValue();
			} else if (type == int.class || type == Integer.class) {
				return number.intValue();
			} else if (type == long.class || type == Long.class) {
				return number.longValue();
			} else if (type == float.class || type == Float.class) {
				return number.floatValue();
			} else if (type == double.class || type == Double.class) {
				return number.doubleValue();
			} else if (type == BigInteger.class) {
				return BigInteger.valueOf(number.longValue());
			} else if (type == BigDecimal.class) {
				return BigDecimal.valueOf(number.doubleValue());
			} else if (type == Date.class) {
				return new Date(number.longValue());
			}
		} else if (value instanceof Collection) {
			final Collection collection = (Collection) value;
			if (type.isArray()) {
				final int length = collection.size();
				final Object array = Array.newInstance(type.getComponentType(),
						length);
				int i = 0;
				for (final Object item : collection) {
					Array.set(array, i++, item);
				}
				return array;
			} else if (!type.isInterface()) {
				try {
					final Collection result = (Collection) type.newInstance();
					result.addAll(collection);
					return result;
				} catch (final Throwable e) {
				}
			} else if (type == List.class) {
				return new ArrayList<Object>(collection);
			} else if (type == Set.class) {
				return new HashSet<Object>(collection);
			}
		} else if (value.getClass().isArray()
				&& Collection.class.isAssignableFrom(type)) {
			Collection collection;
			if (!type.isInterface()) {
				try {
					collection = (Collection) type.newInstance();
				} catch (final Throwable e) {
					collection = new ArrayList<Object>();
				}
			} else if (type == Set.class) {
				collection = new HashSet<Object>();
			} else {
				collection = new ArrayList<Object>();
			}
			final int length = Array.getLength(value);
			for (int i = 0; i < length; i++) {
				collection.add(Array.get(value, i));
			}
			return collection;
		}
		return value;
	}
}
